package com.atguigu.gmall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.entity.CartInfo;
import com.atguigu.gmall.cart.service.CartBizService;
import com.atguigu.gmall.cart.vo.AddCartSuccessVo;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.feign.product.ProductSkuDetailFeignClient;
import com.atguigu.gmall.product.pojo.SkuInfo;
import com.atguigu.gmall.user.vo.UserAuthInfoVo;
import com.atguigu.gmall.utils.UserAuthUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CartBizServiceImpl implements CartBizService {
    @Autowired
    ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    ProductSkuDetailFeignClient productSkuDetailFeignClient;

    @Override
    public AddCartSuccessVo addToCart(Long skuId, Integer skuNum, String cartKey) {
        AddCartSuccessVo addCartSuccessVo = new AddCartSuccessVo();

        log.info("1. 得到操作购物车用的Key：Cart：Info：用户表示");

        log.info("2. 给购物车添加商品{},{}件",skuId,skuNum);
        SkuInfo skuInfo = addCartItem(cartKey,skuId,skuNum);
        
        log.info("3.返回数据");
        addCartSuccessVo.setSkuInfo(skuInfo);
        addCartSuccessVo.setSkuNum(skuNum);
        return addCartSuccessVo;
    }

    /**
     *
     * 查询购物车的商品列表
     * @return
     */
    @Override
    public List<CartInfo> getCartList(String cartKey) {
        List<CartInfo> cartInfos = redisTemplate.opsForHash().values(cartKey)
                .stream()
                .map((item) -> JSON.parseObject(item.toString(), CartInfo.class))
                .sorted(((o1, o2) -> o1.getUpdateTime().compareTo(o2.getUpdateTime())))
                .collect(Collectors.toList());

        //同步最新的价格
        CompletableFuture.runAsync(()->syncPrice(cartKey, cartInfos),threadPoolExecutor);
        return cartInfos;
    }

    private void syncPrice(String cartKey, List<CartInfo> cartInfos) {
        cartInfos.stream()
                .forEach(item->{
                    BigDecimal data = productSkuDetailFeignClient.getPrice(item.getSkuId()).getData();
                    if (Math.abs(item.getSkuPrice().doubleValue() - data.doubleValue()) >= 0.0001){
                        item.setCartPrice(data);
                        redisTemplate.opsForHash().put(cartKey,item.getSkuId(), JSON.toJSONString(item));
                    }
                });
    }

    @Override
    public void updateItemNum(Long skuId, Integer num, String cartKey) {
        CartInfo cartInfo = JSON.parseObject(redisTemplate.opsForHash().get(cartKey, skuId.toString()).toString(), CartInfo.class);
        cartInfo.setSkuNum(cartInfo.getSkuNum() + num);
        redisTemplate.opsForHash().put(cartKey,skuId.toString(), JSON.toJSONString(cartInfo));
    }

    /**
     * 修改按钮章台
     * @param cartKay
     * @param skuId
     * @param checked
     */
    @Override
    public void checkItem(String cartKay, Long skuId, Integer checked) {
        Object redisForCart = redisTemplate.opsForHash().get(cartKay, skuId.toString());
        if (redisForCart != null){
            CartInfo cartInfo = JSON.parseObject(redisForCart.toString(), CartInfo.class);
            cartInfo.setIsChecked(checked);
            redisTemplate.opsForHash().put(cartKay,skuId.toString(), JSON.toJSONString(cartInfo));
        }
    }

    @Override
    public void deleteItem(String cartKey, String skuId) {
        redisTemplate.opsForHash().delete(cartKey,skuId);
    }

    @Override
    public void deleteChecked(String cartKey) {
        List<String> collect = getCheckedId(cartKey);

        redisTemplate.opsForHash().delete(cartKey,collect.toArray());
    }

    /**
     * 获取选中的物品Id列表。
     * @param cartKey
     * @return
     */
    public List<String> getCheckedId(String cartKey) {
        List<CartInfo> cartList = getCartList(cartKey);
        List<String> collect = cartList.stream()
                .filter(o1 -> o1.getIsChecked() == 1)
                .map(item -> item.getSkuId().toString())
                .collect(Collectors.toList());
        return collect;
    }

    /**
     * 获取选中的物品列表。
     * @param cartKey
     * @return
     */
    @Override
    public List<CartInfo> getCheckeds(String cartKey) {
        List<CartInfo> cartList = getCartList(cartKey);
        List<CartInfo> collect = cartList.stream()
                .filter(o1 -> o1.getIsChecked() == 1)
                 .collect(Collectors.toList());
        return collect;
    }

    /**
     * 展示购物车所有的东西，包括合并
     * @return
     */
    @Override
    public List<CartInfo> displayItems() {
        //1. 先判断用户是否登录了。
        UserAuthInfoVo userAuthInfo = UserAuthUtils.getUserAuthInfo();
        Long userId = userAuthInfo.getUserId();
        String userTempId = userAuthInfo.getUserTempId();
        String userIdKey = RedisConst.CART_INFO + userId;
        String userTempIdKey = RedisConst.CART_INFO + userTempId;

        //2. 判断临时购物车有没有东西
        if (StringUtils.isEmpty(userId)){
            if (redisTemplate.getExpire(userTempIdKey) < 0){
                redisTemplate.expire(userTempIdKey,365, TimeUnit.DAYS);
            }
            List<CartInfo> collect = redisTemplate.opsForHash().values(userTempIdKey)
                    .stream()
                    .map(item -> JSON.parseObject((String) item, CartInfo.class))
                    .collect(Collectors.toList());
            return collect;
        }

        //3. 用户登录判断临时购物车是否合并
        Long size = redisTemplate.opsForHash().size(userTempIdKey);
        if (size > 0){
            //执行合并
            //1.迁移购物车内容
            List<CartInfo> cartList = getCartList(userTempIdKey);
            for (CartInfo cartInfo : cartList) {
                addToCart(cartInfo.getSkuId(),cartInfo.getSkuNum(),userIdKey);
            }
            //2.删除临时购物车内容
            redisTemplate.delete(userTempIdKey);
        }
        return getCartList(userIdKey);
    }

    /**
     * 添加商品到购物车
     * @param cartKey
     * @param skuId
     * @param skuNum
     * @return
     */
    private SkuInfo addCartItem(String cartKey, Long skuId, Integer skuNum) {
        SkuInfo skuInfo = null;

        // 购物车中有无此商品。
        BoundHashOperations<String, String, String> cart = BoundHashOperations(cartKey);
        if (cart.hasKey(skuId.toString())){

            //?有，增加。
            log.info("原购物车中有，修改数量增加");
            CartInfo cartInfo = getCartInfo(cartKey,skuId);
            assert cartInfo != null;
            int skuCount = cartInfo.getSkuNum() + skuNum;
            if (skuCount > RedisConst.CART_ITEM_NUM_LIMIT){
                //如果Sku单种数量超过200报错。
                throw new GmallException(ResultCodeEnum.CART_ITEM_NUM_OVERFLOW);
            }

            cartInfo.setSkuNum(skuCount);

            // 获取实时价格。
            cartInfo.setSkuPrice(productSkuDetailFeignClient.getPrice(skuId).getData());

            String cartJson = JSON.toJSONString(cartInfo);
            cart.put(String.valueOf(skuId),cartJson);
            skuInfo = convertCartInfo2SkuInfo(cartInfo);

        }else {

            //?无，创建。
            log.info("原购物车没有，新增一个Sku");
            Result<SkuInfo> resultSkuInfo = productSkuDetailFeignClient.getSkuInfo(skuId);
            // 保存到购物车
            CartInfo cartInfo = convertSkuInfo2CartInfo(resultSkuInfo.getData());
            cartInfo.setSkuNum(skuNum);

            String cartInfoJson = JSON.toJSONString(cartInfo);
            Long size = redisTemplate.opsForHash().size(cartKey);
            cart.put(skuId.toString(),cartInfoJson);

            if (size > RedisConst.CART_ITEM_NUM_SIZE){
                redisTemplate.opsForHash().delete(cartKey,cartInfo.getSkuId().toString());
                //购物车内Sku类型上限为200种
                throw new GmallException(ResultCodeEnum.CART_ITEM_NUM_OVERFLOW);
            }


            skuInfo = resultSkuInfo.getData();
        }

        return skuInfo;
    }

    private SkuInfo convertCartInfo2SkuInfo(CartInfo cartInfo) {
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setId(cartInfo.getSkuId());
        skuInfo.setPrice(cartInfo.getSkuPrice());
        skuInfo.setSkuName(cartInfo.getSkuName());
        skuInfo.setSkuDefaultImg(cartInfo.getImgUrl());
        return skuInfo;
    }

    /**
     * 从制定购物车得到商品
     * @param cartKey
     * @param skuId
     * @return
     */
    private CartInfo getCartInfo(String cartKey, Long skuId) {
        Object redisForCart = redisTemplate.opsForHash().get(cartKey, skuId.toString());
        if (redisForCart == null){
            return null;
        }else {
            String cartInfoJson = redisForCart.toString();
            CartInfo cartInfo = JSON.parseObject(cartInfoJson, CartInfo.class);
            return cartInfo;
        }

    }

    private CartInfo convertSkuInfo2CartInfo(SkuInfo data) {
        CartInfo cartInfo = new CartInfo();
        cartInfo.setSkuId(data.getId());
        cartInfo.setCartPrice(data.getPrice());
        cartInfo.setSkuPrice(data.getPrice());
        cartInfo.setImgUrl(data.getSkuDefaultImg());
        cartInfo.setSkuName(data.getSkuName());
        cartInfo.setIsChecked(1);
        cartInfo.setCreateTime(new Date());
        cartInfo.setUpdateTime(new Date());
        return cartInfo;
    }


    /**
     * 获取当前购物车
     * @param cartKey
     * @return
     */
    private BoundHashOperations<String, String, String> BoundHashOperations(String cartKey) {
        BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(cartKey);
        return hashOps;
    }

    /**
     * 1. 得到操作购物车用的Key：Cart：Info：用户表示
     * @return
     */
    public static String determinCartKey() {
        String cartKey = RedisConst.CART_INFO;

        UserAuthInfoVo userAuthInfo = UserAuthUtils.getUserAuthInfo();
        if (!StringUtils.isEmpty(userAuthInfo.getUserId())){
            cartKey = cartKey + userAuthInfo.getUserId();
        }else {
            cartKey = cartKey + userAuthInfo.getUserTempId();
        }
        return cartKey;
    }
}
